from __future__ import print_function

## Import list
from cli import CLI
from os.path import expanduser, getsize, abspath, exists, dirname, isdir, basename
from os import getcwd, makedirs, remove, walk, stat, link, symlink, unlink, system
import logging
import string
import ConfigParser
import utils
from shutil import copy2

class Media(CLI):

    def addTag(self):
        if not self._args.hash:
            logging.critical("media descr requires --hash")
            raise self.CLIExceptionMissingArg("hash")
        if not self._args.tag:
            logging.critical("media descr requires --tag")
            raise self.CLIExceptionMissingArg("tag")
        for tag in self._args.tag:
            try:
                self._db.mediaAddTag(self._args.hash, tag)
            except:
                raise

    def checkRepo(self):
        logging.info("Generating media list... ")
        print ("Generating media list... ", end='')
        mediaList = self._db.mediaInfo()
        totalMediaInDB = len(mediaList)
        print ("Done")

        print ("Generating repo list... ", end='')
        fileList = []
        for root, dirs, files in walk(self._pdriveFiles):
            if len(files) < 1:
                continue
            for singleFile in files:
                fileList.append("{}/{}".format(root, singleFile))
        print ("Done")
        fqpnList = []
        print ("Comparing dbase to repo")
        count = 0
        for media in mediaList:
            fqpn = utils.getFqpnFromMediaObject(self._pdriveFiles, media)
            fqpnList.append(fqpn)
            sha256Hash = utils.sha256HashFile(fqpn)
            if fqpn not in fileList:
                print("{} is not in repo".format(media))
            if sha256Hash != media["sha256Hash"]:
                print("{} sha256 hash mis-match. File has changed".format(media))
            count = count + 1
            utils.printSimplePercentComplete(count, totalMediaInDB)

        print ("\nComparing repo to dbase")
        count = 0
        totalMediaInRepo = len(fileList)
        for media in fileList:
            if media not in fqpnList:
                print ("{} is not in db".format(media))
                continue
            count = count + 1
            utils.printSimplePercentComplete(count, totalMediaInRepo)
            
        raise SystemExit

        mediaStatus = {}
        print ("Fetched {} entries from the database".format(totalMediaInDB))
        count = 0
        for media in mediaList:
            detail = {}
            hmac = utils.hmacSHA256(str(media["filesize"]), media["sha256Hash"])
            fqpn = "{}/{}/{}.{}".format(pdriveFiles, hmac[0], hmac, media["fileType"])
            detail["FQPN"] = fqpn
            if not exists(fqpn):
                detail['FileExist'] = False
            else:
                detail['FileExist'] = True
            if utils.sha256HashFile(fqpn) != media["sha256Hash"]:
                detail['sha256Match'] = False
            else:
                detail['sha256Match'] = True
            mediaStatus[media["sha256Hash"]] = detail
            count = count + 1
            utils.printSimplePercentComplete(count, totalMediaInDB)

        print("\n\nWalking repo...")
        totalFiles = 0
        for root, dirs, files in walk(pdriveFiles):
            if len(files) < 1:
                continue
            totalFiles = totalFiles + len(files)
        count = 0
        for root, dirs, files in walk(pdriveFiles):
            if len(files) < 1:
                continue
            for singleFile in files:
                detail = {}
                fqpn = "{}/{}".format(root, singleFile)
                fileStats = stat(fqpn)
                sha256Hash = utils.sha256HashFile(fqpn)
                mime = utils.fileMime(fqpn)
                fileType = string.split(mime, "/")[1]
                hmac = utils.hmacSHA256(str(fileStats.st_size), sha256Hash)
                filename = "{}.{}".format(hmac, fileType)
                detail["FQPN"] = fqpn
                if singleFile != filename:
                    detail["HMACMatch"] = False
                else:
                    detail["HMACMatch"] = True
                if len(db.mediaInfo(sha256Hash = sha256Hash)) == 0:
                    detail["InDB"] = False
                else:
                    detail["InDB"] = True
                mediaStatus[sha256Hash] = detail
                count = count + 1
                utils.printSimplePercentComplete(count, totalFiles)
        print("\n")

        print("Checking media...")
        for media in mediaStatus:
            if not mediaStatus[media]["FileExist"]:
                print("{}: File Not Exist In Repo".format(media))
            if not mediaStatus[media]["sha256Match"]:
                print("{}: Sha256 Hash Mismatch; file has changed".format(media))
            if not mediaStatus[media]["HMACMatch"]:
                print("{}: HMAC Mismatch; file has changed".format(media))
            if not mediaStatus[media]["InDB"]:
                print("{}: File not in DB; orphaned".format(media))

    def dscr(self):
        if not self._args.hash:
            logging.critical("media descr requires --hash")
            raise self.CLIExceptionMissingArg("hash")
        try:
            descr = self._db.mediaDescr(self._args.hash, self._args.descr)
            print (descr)
        except:
            raise

    def details(self):
        if not self._args.hash:
            logging.critical("media descr requires --hash")
            raise self.CLIExceptionMissingArg("hash")
        try:
            for details in self._db.mediaInfo(sha256Hash = self._args.hash):
                fqpn = utils.getFqpnFromMediaObject(self._pdriveFiles, details)
                tags = ""
                for tag in sorted(details["tagList"]):
                    tags = "{} | {}".format(tags, tag)
                tags = string.strip(tags," | ")
                tags = string.strip(tags)
            
                inS3 = "No"
                if details["s3"] == self._args.hash:
                    inS3 = "Yes"

                print("Detail for hash {}".format(self._args.hash))
                print("Date Commited to Database: {}".format(utils.timeSinceEpoch(details["commitDate"])))
                print("Fully Qualified Path Name: {}".format(fqpn));
                print("Media Type: {}".format(details["mediaType"]))
                print("File Type: {}".format(details["fileType"]))
                print("Imported Directory: {}".format(details["directory"]))
                print("Imported Filename: {}".format(details["filename"]))
                print("File Size: {} ({})".format(details["filesize"], utils.humanSize(details["filesize"], 2)))
                print("Access Time: {}".format(utils.timeSinceEpoch(details["atime"])))
                print("Modify Time: {}".format(utils.timeSinceEpoch(details["mtime"])))
                print("Change Time: {}".format(utils.timeSinceEpoch(details["chtime"])))
                print("Description: {}".format(details["descr"]))
                print("Rating: {}".format(details["rating"]))
                print("Associated Tags: {}".format(tags))
                print("Sync'ed to S3: {}".format(inS3))
        except:
            raise

    def export(self):
        if not self._args.localdir:
            logging.critical("media descr requires --localdir")
            raise self.CLIExceptionMissingArg("localdir")
        
        ## Check for the existance of the target dir
        ## Create it, if needed
        localDir = self._args.localdir
        if exists(localDir):
            if not isdir(localDir):
                raise self.CLIExceptionNotADir(localDir)
        else:
            try:
                makedirs(localDir)
            except:
                raise

        ## Write out a README file; contains stats and info
        with open(localDir + "/README", "w") as fp:
            fp.write("Media list generated with these constraints...\ntags: {}\n".format(str(self._args.tag)))
            fp.write("Search: {}\n".format(str(self._args.search)))
            fp.write("Hash: {}\n".format(str(self._args.hash)))
            fp.write("File type: {}\n".format(str(self._args.filetype)))
            fp.write("Media type: {}\n".format(str(self._args.mediatype)))
            fp.write("To directory: {}\n".format(str(localDir)))
            if self._args.hard:
                fp.write("Using hard links\n")
            elif self._args.copy:
                fp.write("Using copy\n")
            else:
                fp.write("Using soft/sym links\n")
            if self._args.onedir:
                fp.write("To a single directory\n")
            fp.write("\nSee file 'hashes.txt' for a list of hashes exported\n")
            fp.write("\nTo create a tarball (assuming softlinks were created):\n")
            fp.write("tar cfh ~/export.tar --exclude=.prelink export\n")

        ## Generate the media list
        logging.info("Generating media list...")
        hashes = []
        for details in self._db.mediaInfo(
            sha256Hash = self._args.hash, 
            tagLimitList = self._args.tag, 
            mediaType =  self._args.mediatype, 
            search = self._args.search, 
            fileType = self._args.filetype
        ):
            origDir = details["directory"]
            fqpn = utils.getFqpnFromMediaObject(self._pdriveFiles, details)
            if self._args.onedir:
                linkRootDir = "{}".format(localDir)
            else:
                linkRootDir = "{}/{}".format(localDir, origDir)
            linkName = "{}/{}".format(linkRootDir, details["filename"])
            if not exists(linkRootDir):
                try:
                    makedirs(linkRootDir)
                except:
                    raise
            if self._args.hard:
                link(fqpn, linkName)
            elif self._args.copy:
                copy2(fqpn, linkName)
            else:
                prelinkDir = "{}/.prelink".format(localDir)
                if not exists(prelinkDir):
                    try:
                        makedirs(prelinkDir)
                    except:
                        raise
                prelink = "{}/{}".format(abspath(prelinkDir), details["sha256Hash"])
                symlink(fqpn, prelink)
                symlink(prelink, linkName)
            hashes.append(details["sha256Hash"])

            for thumbDetails in self._db.getThumbnailAllSize(details["sha256Hash"]):
                fqpn = utils.getFqpnFromMediaObject(self._pdriveFiles, thumbDetails)
                if self._args.onedir:
                    linkRootDir = "{}/thumbnails".format(localDir)
                else:
                    linkRootDir = "{}/{}/thumbnails".format(localDir, origDir)
                linkName = "{}/{}-{}.png".format(linkRootDir, thumbDetails["thumbsize"], details["filename"])
                if not exists(linkRootDir):
                    try:
                        makedirs(linkRootDir)
                    except:
                        raise
                if self._args.hard:
                    link(fqpn, linkName)
                elif self._args.copy:
                    copy2(fqpn, linkName)
                else:
                    prelinkDir = "{}/.prelink".format(localDir)
                    if not exists(prelinkDir):
                        try:
                            makedirs(prelinkDir)
                        except:
                            raise
                    prelink = "{}/{}".format(abspath(prelinkDir), thumbDetails["sha256Hash"])
                    symlink(fqpn, prelink)
                    symlink(prelink, linkName)


        ## Write out a file which contains a list of hashes exported
        with open(localDir + "/mediaHashes.txt", "w") as fp:
            for hash in hashes:
                fp.write("{}\n".format(hash))

    def ls(self):
        try:
            if not self._args.quiet:
                print("Generating media list...")
            for details in self._db.mediaInfo(
                sha256Hash = self._args.hash, 
                tagLimitList = self._args.tag, 
                mediaType =  self._args.mediatype, 
                search = self._args.search, 
                fileType = self._args.filetype
            ):
                tags = ""
                for tag in sorted(details["tagList"]):
                    tags = "{} | {}".format(tags, tag)
                tags = tags[3:]
                if not self._args.quiet:
                    baseLine = "{} | {} | {}".format(details["sha256Hash"], details["mediaType"], tags)
                    if self._args.showrate:
                        baseLine = "{} | {}".format(baseLine, details["rating"])
                    if self._args.showdescr:
                        baseLine = "{} | {}".format(baseLine, details["descr"])
                    print(baseLine)
                    
                else:
                    print ("{}".format(details["sha256Hash"]))
        except:
            raise

    def lsFileTypes(self):
        try:
            for row in self._db.lsFileTypes():
                print(row["type"])
        except:
            pass

    def lsMediaTypes(self):
        try:
            for row in self._db.lsMediaTypes():
                print(row["type"])
        except:
            raise
        
    def mport(self):
        localDir = self._args.localdir
        importPath = self._cfg.importPath

        ## Sanity checks
        logging.debug("Sanity checks...")
        if localDir:
            importPath = localDir
        elif not importPath:
            if not localDir:
                logging.critical("media descr requires --localdir")
                raise self.CLIExceptionMissingArg("localdir")
        importPath = str(importPath)
        if not exists(importPath):
            logging.critical("Path '{}' does not exist".format(importPath));
            raise self.CLIExceptionPathNotExist(importPath)
        if not isdir(importPath):
            logging.critical("Path '{}' is not a directory".format(importPath))
            raise self.CLIExceptionNotADir(importPath)

        logging.debug("Sanity checks passed. Waiting for user confirmation.")
        print("I will import from '{}'".format(importPath))
        userConfirm = "-"
        while userConfirm[0] not in ['y', 'Y', 'n', 'N']:
            userConfirm = raw_input("Do you wish to continue? (y/n): ")
        if userConfirm[0] in ['n', 'N']:
            raise self.CLIExceptionUserCancel()
        
        logging.debug("Everything seems to be in place... let's give it a shot")
        fileList = []
        try:
            totalFiles = 0
            for root, dirs, files in walk(importPath):
                if len(files) < 1:
                    continue
                totalFiles = totalFiles + len(files)
            logging.info("{} files to process".format(totalFiles))
            print("{} files to process".format(totalFiles))
            
            fileCount = 0   
            for root, dirs, files in walk(importPath):
                if len(files) < 1:
                    continue
                tags = string.split(string.replace(root, importPath + "/", ""), "/")
                for singleFile in files:
                    fqpn = "{}/{}".format(root, singleFile)
                    fileStats = stat(fqpn)
                    mime = utils.fileMime(fqpn)
                    mediaType = string.split(mime, "/")[0]
                    fileType = string.split(mime, "/")[1]
                    sha256Hash = utils.sha256HashFile(fqpn)
                    fileDestFqpn = utils.getFqpnFromData(self._pdriveFiles, fileStats.st_size, sha256Hash, fileType)
                    fileDestDir = dirname(fileDestFqpn)
                    if not exists(fileDestDir):
                        try:
                            makedirs(fileDestDir)
                        except OSError as e:
                            if e.errno != 17: # Already Exists
                                raise
                    if not exists(fileDestFqpn):
                        try:
                            copy2(fqpn, fileDestFqpn)   
                        except:
                            raise
            
                    fileCount = fileCount + 1
                    self._db.newMedia(
                        sha256Hash, 
                        mediaType, 
                        fileType, 
                        tags, 
                        string.replace(root, importPath + "/", ""), 
                        singleFile, 
                        int(fileStats.st_size), 
                        int(fileStats.st_atime), 
                        int(fileStats.st_mtime), 
                        int(fileStats.st_ctime)
                    )
                    fileList.append({"sha256Hash": sha256Hash, "importFQPN": fqpn, "repoFQPN": fileDestFqpn})
                    utils.printSimplePercentComplete(fileCount, totalFiles)
            print("\nComplete!                   ")
            print("Import list:")
            for media in fileList:
                print("{} | {} | {}".format(media["sha256Hash"], media["importFQPN"], media["repoFQPN"]))
        except:
            raise

    def pathTo(self):
        if not self._args.hash:
            logging.critical("media descr requires --hash")
            raise self.CLIExceptionMissingArg("hash")
        try:
            endChar = ''
            if self._args.nl is True:
                endChar = "\n"
            print ("{}".format(utils.getFqpnFromMediaObject(self._pdriveFiles, self._db.mediaInfo(self._args.hash)[0])), end=endChar)
        except:
            raise

    def rate(self):
        try:
            if not self._args.hash:
                logging.critical("media rate requires --hash")
                raise self.CLIExceptionMissingArg("hash")
            rate = self._db.mediaRate(slef._args.hash, self_.args.rate)
            print (rate)
        except:
            raise

    def rm(self):
        try:
            if not self._args.hash:
                logging.critical("media descr requires --hash")
                raise self.CLIExceptionMissingArg("hash")
            for details in self._db.mediaInfo(sha256Hash = self._args.hash):
                fqpn = utils.getFqpnFromMediaObject(self._pdriveFiles, details)
                try:
                    self._db.mediaRm(self._args.hash)
                    logging.info("Reference to hash removed from dbase")
                    unlink(fqpn)
                    logging.info("File ({}) unlinked".format(fqpn))
                    if self._args.s3:
                        #db.mediaRmFromS3(args.hash)
                        #s3.unlink(hmac.details["fileType"])
                        pass
                except:
                    logging.critical("Failed to fully remove media. Hash ({}) File ({}). Manual cleanup may be required".format(self._args.hash, fqpn))
                    raise
            print ("Media removed")
        except:
            raise

    def rmTag(self):
        if not self._args.hash:
            logging.critical("media descr requires --hash")
            raise self.CLIExceptionMissingArg("hash")
        if not self._args.tag:
            logging.critical("media descr requires --tag")
            raise self.CLIExceptionMissingArg("tag")
        for tag in self._args.tag:
            self._db.mediaRMTag(self._args.hash, tag)

    def mkHtml(self):
        if not self._args.localdir:
            logging.critical("media mkhtml  requires --localdir")
            raise self.CLIExceptionMissingArg("localdir")
        localDir = self._args.localdir
        links = ""
        with open(self._args.localdir + "/mediaHashes.txt") as fp:
            for hash in fp:
                hash = string.strip(hash)
                for details in self._db.mediaInfo(sha256Hash = hash):
                    origDir = details["directory"]
                    fqpn = utils.getFqpnFromMediaObject(self._pdriveFiles, details)
                    linkRootDir = "{}".format(origDir)
                    linkName = "{}/{}".format(linkRootDir, details["filename"])
                    thumbs = ""
                    thumbDetails = self._db.getThumbnail(details["sha256Hash"], 3)
                    linkRootDirThumb = "{}/thumbnails".format(origDir)
                    linkNameThumb3 = "{}/{}-{}.png".format(linkRootDirThumb, "3", details["filename"])
                    linkNameThumb1 = "{}/{}-{}.png".format(linkRootDirThumb, "1", details["filename"])
                    links = links + '''
            <li class="span3">
                 <a href="{}" class="thumbnail">
                    <img class="lazy" src="gray.gif" data-original="{}">
                 </a>
            </li>'''.format(linkNameThumb1,linkNameThumb3)
        html = '''<!DOCTYPE html>
<html lang="en">
    <head>
        <link href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combined.min.css" rel="stylesheet">
    </head>
    <body>
        <ul class="thumbnails">
            {}
        </ul>
        <script src="http://code.jquery.com/jquery.js"></script>
        <script src="jquery.lazyload.js"></script>
        <script src="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/js/bootstrap.min.js"></script>
        <script src="script.js"></script>
    </body>
</html>
 '''.format(links)
        print(html)
